package geektime.concurrent.part3;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 复杂的CompletableFuture应用
 */
public class ComplicatedCompletableFutureDemo {

    public static void main(String[] args) {
        long t0 = System.currentTimeMillis();
        cars().thenCompose(cars -> {
            List<CompletionStage<Car>> updatedCars = cars.stream()
                // rating方法返回的是CompletionStage<Float>
                .map(car -> rating(car.manufacturedId).thenApply(r -> {
                    car.setRating(r);
                    return car;
                })).collect(Collectors.toList());
            // 让上面所有的CompletionStage<Car>都完成后再进行后续步骤
            CompletableFuture<Void> done = CompletableFuture
                .allOf(updatedCars.toArray(new CompletableFuture[updatedCars.size()]));
            return done.thenApply(v -> updatedCars.stream()
                .map(CompletionStage::toCompletableFuture)
                .map(CompletableFuture::join)
                .collect(Collectors.toList()));
        }).whenComplete((cars, th) -> {
            if (th == null) {
                cars.forEach(System.out::println);
            } else {
                throw new RuntimeException(th);
            }
        }).toCompletableFuture().join();
        long t1 = System.currentTimeMillis();
        System.out.printf("Time cost=%s\n", (t1 - t0));
    }

    public static CompletionStage<Float> rating(int manufacturedId) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            switch (manufacturedId) {
                case 2:
                    return 4f;
                case 3:
                    return 4.1f;
                case 7:
                    return 4.2f;
                default:
                    return 5f;
            }
        }).exceptionally(fn -> -1f);
    }

    public static CompletionStage<List<Car>> cars() {
        List<Car> cars = new ArrayList<>();
        cars.add(new Car(1, 3, "Fiesta", 2017));
        cars.add(new Car(2, 7, "Camry", 2014));
        cars.add(new Car(3, 2, "M2", 2008));
        return CompletableFuture.supplyAsync(() -> cars);
    }
}
